package tools

import (
	"context"
	"time"

	// Redis 6.0及以下版本：选择Go-redis v8.0及以下版本。
	"github.com/go-redis/redis/v8"
	redsync "github.com/go-redsync/redsync/v4"

	// Redis 7.0及以上版本：选择Go-redis v9.0及以上版本。
	// "github.com/go-redsync/redsync/v4/redis/goredis/v9"
	// "github.com/redis/go-redis/v9"
	"github.com/go-redsync/redsync/v4/redis/goredis/v8"
)

/*
 +----------------------------------------------------------------------
 + Title        : redis 缓存【结构体封装】
 + Author       : huwl
 + Version      : 1.0.0
 + Initial-Time : 2021-09-22
 + Last-time    : 这个文件最后修改的时间 + 修改人的名称
 + Desc         : redis 缓存 文档地址【https://redis.uptrace.dev/zh/guide/go-redis.html】
 + 重点：Redis 6.0及以下版本：选择Go-redis v8.0及以下版本。
 		Redis 7.0及以上版本：选择Go-redis v9.0及以上版本。
		优点：
			解耦：通过接口隐藏实现，业务层依赖接口而非具体实现
			可测试性：轻松Mock接口进行单元测试
			灵活扩展：支持多实例、动态配置
			生命周期可控：结构体可包含连接关闭逻辑
		缺点：
			代码量稍多（需定义结构体和方法）
			需显式传递依赖（可通过依赖注入框架简化）
 +----------------------------------------------------------------------
*/

var RedisMode string
var RsPool *redsync.Redsync

var ctx = context.Background()

// redis 配置信息
type RedisConf struct {
	Mode   string `yaml:"mode" json:"mode"`
	Single struct {
		Host     string `yaml:"host" json:"host"`
		Password string `yaml:"password" json:"password"`
		Db       int64  `yaml:"db" json:"db"`
	}

	Replication struct {
		Host     string `yaml:"host" json:"host"`
		Password string `yaml:"password" json:"password"`
		Db       int64  `yaml:"db" json:"db"`
	}

	Sentinel struct {
		Host       []string `yaml:"host" json:"host"`
		Password   string   `yaml:"password" json:"password"`
		MasterName string   `yaml:"master_name" json:"master_name"`
		Db         int64    `yaml:"db" json:"db"`
	}

	Cluster struct {
		Host     []string `yaml:"host" json:"host"`
		Password string   `yaml:"password" json:"password"`
		Db       int64    `yaml:"db" json:"db"`
	}
}

type RedisEntry struct {
	redisClient        *redis.Client
	redisClusterClient *redis.ClusterClient
}

// 初始化 redis 连接
func NewRedisEntry() (*RedisEntry, error) {
	// +++++++++++++++++ 使用本地配置方式开始 +++++++++++++++++
	// //读取 redis 配置
	// cfg, err := GetConfig("redis", "yml")
	// if err != nil {
	// 	panic(err.Error())
	// }

	// //模式【single、replication、sentinel、cluster】
	// mode := cfg.GetString("mode")
	// +++++++++++++++++ 使用本地配置方式结束 +++++++++++++++++

	// +++++++++++++++++ 使用nacos配置方式开始 ++++++++++++++++
	var err error
	// 读取 redis 配置
	redisConf := &RedisConf{}
	InitConfigCenter(RedisConfigCenter, redisConf)
	mode := redisConf.Mode
	// +++++++++++++++++ 使用nacos配置方式结束 ++++++++++++++++

	RedisMode = mode

	redisEntry := &RedisEntry{}

	//连接redis
	if mode == "single" { // 单机版
		redisEntry.redisClient = redis.NewClient(&redis.Options{
			// 本地配置文件模式
			// Addr:     cfg.GetString(Env + "." + mode + ".host"),
			// Password: cfg.GetString(Env + "." + mode + ".password"),
			// DB:       cfg.GetInt(Env + "." + mode + ".db"),

			// nacos配置中心模式
			Addr:     redisConf.Single.Host,
			Password: redisConf.Single.Password,
			DB:       int(redisConf.Single.Db),
		})
		_, err = redisEntry.redisClient.Ping(ctx).Result()

	} else if mode == "sentinel" { // 哨兵版
		redisEntry.redisClient = redis.NewFailoverClient(&redis.FailoverOptions{

			// 本地配置文件模式
			// MasterName: cfg.GetString(Env + "." + mode + ".master_name"),
			// SentinelAddrs: cfg.GetStringSlice(Env + "." + mode + ".host"),
			// Password:      cfg.GetString(Env + "." + mode + ".password"),
			// DB:            cfg.GetInt(Env + "." + mode + ".db"),

			// nacos配置中心模式
			MasterName:    redisConf.Sentinel.MasterName,
			SentinelAddrs: redisConf.Sentinel.Host,
			Password:      redisConf.Sentinel.Password,
			DB:            int(redisConf.Sentinel.Db),
		})
		_, err = redisEntry.redisClient.Ping(ctx).Result()

	} else if mode == "cluster" { // 分片集群版
		redisEntry.redisClusterClient = redis.NewClusterClient(&redis.ClusterOptions{
			// 本地配置文件模式
			// Addrs:    cfg.GetStringSlice(Env + "." + mode + ".host"),
			// Password: cfg.GetString(Env + "." + mode + ".password"),

			// nacos配置中心模式
			Addrs:    redisConf.Cluster.Host,
			Password: redisConf.Cluster.Password,
		})

		_, err = redisEntry.redisClusterClient.Ping(ctx).Result()
	}

	pool := goredis.NewPool(redisEntry.redisClient)
	RsPool = redsync.New(pool)
	return redisEntry, err
}

/**
 * 设置值
 *
 * @param string key ----------------------------------------------------必传
 * @param []interface{} value -------------------------------------------必传
 * @param time.Duration expiration --------------------------------------必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisSet(key string, value interface{}, expiration time.Duration) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.Set(ctx, key, value, expiration).Err()
	}

	return re.redisClusterClient.Set(ctx, key, value, expiration).Err()
}

/**
 * 获取值
 *
 * @param string key ----------------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisGet(key string) (string, error) {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.Get(ctx, key).Result()
	}

	return re.redisClusterClient.Get(ctx, key).Result()
}

/**
 * 删除
 *
 * @param string key ----------------------------------------------------必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisDel(key string) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.Del(ctx, key).Err()
	}

	return re.redisClusterClient.Del(ctx, key).Err()
}

/**
 * 左进列表
 *
 * @param string key ----------------------------------------------------必传
 * @param []interface{} values ------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisLpush(key string, values []interface{}) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.LPush(ctx, key, values).Err()
	}

	return re.redisClusterClient.LPush(ctx, key, values).Err()
}

/**
 * 左出列表
 *
 * @param string key ----------------------------------------------------必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisLpop(key string) (string, error) {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.LPop(ctx, key).Result()
	}

	return re.redisClusterClient.LPop(ctx, key).Result()
}

/**
 * 右进列表
 *
 * @param string key ----------------------------------------------------必传
 * @param []interface{} values ------------------------------------------必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisRpush(key string, values []interface{}) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.RPush(ctx, key, values...).Err()
	}

	return re.redisClusterClient.RPush(ctx, key, values...).Err()
}

/**
 * 右进列表
 *
 * @param string key ----------------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisRpop(key string) (string, error) {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.RPop(ctx, key).Result()
	}

	return re.redisClusterClient.RPop(ctx, key).Result()
}

/**
 * geoadd
 *
 * @param string key ----------------------------------------------------必传
 * @param string name ---------------------------------------------------必传
 * @param float64 longitude ---------------------------------------------必传
 * @param float64 latitude ----------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisGeoadd(key, name string, longitude, latitude float64) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.GeoAdd(ctx, key, &redis.GeoLocation{Name: name, Longitude: longitude, Latitude: latitude}).Err()
	}

	return re.redisClusterClient.GeoAdd(ctx, key, &redis.GeoLocation{Name: name, Longitude: longitude, Latitude: latitude}).Err()
}

/**
 * geodist
 *
 * @param string key ----------------------------------------------------必传
 * @param string member1 ------------------------------------------------必传
 * @param string member2 ------------------------------------------------必传
 * @param string unit ---------------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisGeodist(key, member1, member2, unit string) (float64, error) {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.GeoDist(ctx, key, member1, member2, unit).Result()
	}

	return re.redisClusterClient.GeoDist(ctx, key, member1, member2, unit).Result()
}

/**
 * georadius
 *
 * @param string key ----------------------------------------------------必传
 * @param float64 longitude ---------------------------------------------必传
 * @param float64 latitude ----------------------------------------------必传
 * @param float64 radius ------------------------------------------------必传
 * @param string unit ---------------------------------------------------必传
 * @param withCoord bool ------------------------------------------------必传
 * @param withDist bool -------------------------------------------------必传
 * @param string sort ---------------------------------------------------必传
 * @return string error
 * @author huwl
 */
func (re RedisEntry) RedisGeoradius(key string, longitude, latitude, radius float64, unit string, withCoord, withDist bool, sort string) ([]redis.GeoLocation, error) {
	geoRadiusQuery := &redis.GeoRadiusQuery{
		Radius:    radius,
		Unit:      unit,
		WithCoord: withCoord,
		WithDist:  withDist,
		Sort:      sort,
		// 	WithCoord   bool
		// WithDist    bool
		// WithGeoHash bool
		// Count       int
		// // Can be ASC or DESC. Default is no sort order.
		// Sort      string
		// Store     string
		// StoreDist string
	}
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.GeoRadius(ctx, key, longitude, latitude, geoRadiusQuery).Result()
	}

	return re.redisClusterClient.GeoRadius(ctx, key, longitude, latitude, geoRadiusQuery).Result()
}

/**
 * 删除geo位置(geo本质就是有序集合，因此可以使用有序集合的方式删除)
 *
 * @param string key ----------------------------------------------------必传
 * @param []interface{} members -----------------------------------------非必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisGeoremove(key string, members ...interface{}) error {
	return re.RedisZrem(key, members...)
}

/**
 * 有序集合删除成员
 *
 * @param string key ----------------------------------------------------必传
 * @param []interface{} members -----------------------------------------非必传
 * @return error
 * @author huwl
 */
func (re RedisEntry) RedisZrem(key string, members ...interface{}) error {
	if RedisMode == "single" || RedisMode == "sentinel" {
		return re.redisClient.ZRem(ctx, key, members).Err()
	}

	return re.redisClusterClient.ZRem(ctx, key, members).Err()
}

// 待续。。。
